package com.backend.system.aspect;

import com.alibaba.fastjson.JSONObject;
import com.backend.system.common.*;
import com.backend.system.common.annotation.CLog;
import com.backend.system.common.enums.ActionTypeEnum;
import com.backend.system.common.enums.ModuleEnum;
import com.backend.system.service.IOperateLogService;
import com.backend.system.util.SpELParserUtils;
import com.backend.system.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日志切面
 *
 * @author backend-manage
 */
@Aspect
@Component
@Slf4j
public class LogAspect {
    private static final Pattern BRACES_PATTERN = Pattern.compile("\\{.*?}");

    @Resource(name = "logThreadPoolTaskExecutor")
    private Executor executor;

    @Resource
    private IOperateLogService logService;

    @Pointcut("@annotation(com.backend.system.common.annotation.CLog)")
    private void pointCut() {
    }

    @AfterReturning(value = "pointCut()")
    public void after(JoinPoint joinPoint) {
        try {
            addLog(joinPoint);
        } finally {
            LogRecordContext.clean();
        }
    }


    public void addLog(JoinPoint joinPoint) {
        String logId = LogId.get();
        RequestHeaderUserInfo userInfo = RequestHolder.getUserInfo();
        Map<String, String> logRecordMap = LogRecordContext.get();

        executor.execute(() -> {
            try {
                // 传递logId到异步线程
                LogId.put(logId);

                // 获取方法+入参
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                Object[] args = joinPoint.getArgs();

                // 获取注解
                CLog cLog = signature.getMethod().getDeclaredAnnotation(CLog.class);

                // 获取模版中的参数（如果存在参数），并拼接
                List<String> templateParameters = getTemplateParameters(cLog.template());
                buildTemplateData(templateParameters, signature, args, logRecordMap);
                String template = cLog.template();
                for (String templateParameter : templateParameters) {
                    template = template.replace(templateParameter, logRecordMap.get(templateParameter));
                }

                // 获取module
                String module = getModule(cLog, signature, args, logRecordMap);

                // 获取actionType
                String actionType = getActionType(cLog, signature, args, logRecordMap);

                // 获取actionId
                List<String> actionIds = getActionId(cLog, signature, args, logRecordMap);

                // 获取扩展字段
                JSONObject ext = getExt(cLog, signature, args, logRecordMap);

                if (StringUtil.isNotBlank(template)) {
                    for (String actionId : actionIds) {
                        log.info("记录日志，user={}, template={}, module={}, actionType={}, actionId={}, ext={}", userInfo, template, module, actionType, actionId, ext);
                        addLog(template, actionType, actionId, module, ext);
                        log.info("设置日志数据成功");
                    }
                } else {
                    log.info("设置日志数据失败：不满足注解条件");
                }
            } catch (Exception e) {
                log.warn("设置日志异常:", e);
            }
        });
    }

    private void addLog(String logDesc, String actionType, String actionId, String businessType, JSONObject ext) {
        logService.addLog(logDesc, actionType, actionId, businessType, ext);
    }


    private List<String> getTemplateParameters(String template) {
        List<String> parameters = new ArrayList<>();
        Matcher matcher = BRACES_PATTERN.matcher(template);
        while (matcher.find()) {
            parameters.add(matcher.group());
        }
        return parameters;
    }


    private void buildTemplateData(List<String> parameters, MethodSignature signature, Object[] args, Map<String, String> map) {
        for (String el : parameters) {
            // 如果EL表达式为空，则直接下一个
            if (!StringUtil.isNotBlank(el)) {
                continue;
            }

            String spEl = el;
            // 兼容自定义数据
            spEl = getEl(spEl);
            if (map.containsKey(spEl)) {
                map.put("{" + spEl + "}", map.get(spEl));
                continue;
            }

            // 自定义类处理
            spEl = parseCustomerMethodEl(spEl);

            // El执行
            if (spEl.contains("#")) {
                String value = SpELParserUtils.parse(signature.getMethod(), args, spEl, String.class);
                map.put(el, value);
            } else {
                map.put(el, "");
            }
        }
    }

    private String getModule(CLog cLog, MethodSignature signature, Object[] args, Map<String, String> map) {
        // 设置了module枚举时，优先获取枚举对应的描述
        if (!ModuleEnum.DEFAULT.equals(cLog.module())) {
            return cLog.module().getDesc();
        }

        // 设置了moduleStr时
        if (StringUtil.isNotBlank(cLog.moduleStr())) {
            return cLog.moduleStr();
        }

        // 设置了moduleEl时
        if (StringUtil.isNotBlank(cLog.moduleEl())) {
            try {
                String el = cLog.moduleEl();
                el = getEl(el);
                // 处理自定义的el
                if (map.containsKey(el)) {
                    return map.get(el);
                }

                // 处理自定义方法el
                el = parseCustomerMethodEl(el);

                // 执行el
                return SpELParserUtils.parse(signature.getMethod(), args, el, String.class);
            } catch (Exception e) {
                log.error("日志切面获取module错误", e);
            }
        }
        return null;
    }


    private String getActionType(CLog cLog, MethodSignature signature, Object[] args, Map<String, String> map) {
        // 设置了actionType枚举时，优先获取枚举对应的描述
        if (!ActionTypeEnum.DEFAULT.equals(cLog.actionType())) {
            return cLog.actionType().getDesc();
        }

        // 设置了actionTypeStr时
        if (StringUtil.isNotBlank(cLog.actionTypeStr())) {
            return cLog.actionTypeStr();
        }

        // 设置了actionTypeEl时
        if (StringUtil.isNotBlank(cLog.actionTypeEl())) {
            String el = cLog.actionTypeEl();
            el = getEl(el);
            // 处理自定义的el
            if (map.containsKey(el)) {
                return map.get(el);
            }

            // 处理自定义方法el
            el = parseCustomerMethodEl(el);

            // 执行el
            return SpELParserUtils.parse(signature.getMethod(), args, el, String.class);
        }
        return null;
    }


    private List<String> getActionId(CLog cLog, MethodSignature signature, Object[] args, Map<String, String> map) {
        // 设置了actionIdEl时
        if (StringUtil.isNotBlank(cLog.actionIdEl())) {
            if (map.containsKey(cLog.actionIdEl())) {
                return Collections.singletonList(map.get(cLog.actionIdEl()));
            }
            String el = cLog.actionIdEl();
            el = getEl(el);
            // 处理自定义el
            if (map.containsKey(el)) {
                return Collections.singletonList(map.get(el));
            }

            // 执行el
            return Collections.singletonList(SpELParserUtils.parse(signature.getMethod(), args, el, String.class));
        }

        // 设置了actionIds时
        if (StringUtil.isNotBlank(cLog.actionIds())) {
            String el = getEl(cLog.actionIds());
            if (map.containsKey(el)) {
                return Arrays.asList(map.get(el).split(","));
            }
        }
        return Collections.singletonList(System.currentTimeMillis() * 10 + new Random().nextInt(10000) + "");
    }

    private JSONObject getExt(CLog cLog, MethodSignature signature, Object[] args, Map<String, String> map) {
        // 如果EL表达式为空，则直接结束
        if (!StringUtil.isNotBlank(cLog.ext())) {
            return null;
        }
        String spEl = cLog.ext();
        //兼容自定义数据
        spEl = getEl(spEl);
        if (map.containsKey(spEl)) {
            String value = map.get(spEl);
            if (StringUtil.isNotBlank(value)) {
                try {
                    return JSONObject.parseObject(value);
                } catch (Exception e) {
                    log.info("JSON转换失败:{},{}", value, e.getMessage());
                    return null;
                }
            }
            return null;
        }

        // 自定义类处理
        spEl = parseCustomerMethodEl(spEl);

        // El执行
        if (spEl.contains("#")) {
            String value = SpELParserUtils.parse(signature.getMethod(), args, spEl, String.class);
            if (StringUtil.isNotBlank(value)) {
                try {
                    return JSONObject.parseObject(value);
                } catch (Exception e) {
                    log.info("JSON转换失败:{},{}", value, e.getMessage());
                    return null;
                }
            }
            return null;
        }
        return null;
    }


    private String parseCustomerMethodEl(String el) {
        for (String key : LogCustomerConfig.getCustomerMethod().keySet()) {
            if (el.contains(key)) {
                String className = key.split("\\.")[0];
                el = el.replace(className, "T(" + LogCustomerConfig.getCustomerMethod().get(key) + ")");
            }
        }
        return el;
    }


    private String getEl(String str) {
        str = str.replaceAll("\\{", "");
        str = str.replaceAll("}", "");
        return str;
    }

}
